﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Xml.Linq;
using HtmlAgilityPack;
using NetQueryLib.Expressions;

namespace NetQueryLib
{
	public class NetQuery
	{
		private readonly XElement[] _elements;
		private readonly IEnumerable<SelectorExpressionBuilder> _expressions = new List<SelectorExpressionBuilder>
		                                                                	{
																				new IDSelectorExpressionBuilder(),
																				new ClassNameSelectorExpressionBuilder(),
		                                                                		new ElementNameSelectorExpressionBuilder(),
		                                                                	};

		public NetQuery(string html)
		{
			var document = new HtmlDocument();
			document.LoadHtml(html);

			using (var sw = new StringWriter())
			{
				document.OptionOutputAsXml = true;
				document.Save(sw);

				var data = sw.GetStringBuilder().ToString();
				_elements = new[] {XDocument.Parse(data).Root};
			}
		}

		public NetQuery(params XElement[] elements)
		{
			_elements = elements;
		}

		public XElement[] Items
		{
			get { return _elements; }
		}

		public static string Get(string url)
		{
			using (var client = new WebClient())
			{
				client.Headers.Add(HttpRequestHeader.UserAgent,
				                   "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7");

				return client.DownloadString(url);
			}
		}

		public static T Get<T>(string url, Func<string, T> callback)
		{
			return callback(Get(url));
		}

		public IEnumerable<NetQuery> Find(string selector)
		{
			foreach (var element in _elements)
			{
				var walker = new NodeWalker(element);
				var chunker = new Chunker(selector, _expressions);

				do
				{
					XElement el = walker.CurrentElement;
					Console.WriteLine("Matching " + el.GetPath());

					chunker.Reset();
					var ancestors = el.AncestorsAndSelf().Reverse().ToArray();
					for (var i = 0; i < ancestors.Length; i++)
					{
						var ancestor = ancestors[i];

						if (!chunker.Filter(ancestor))
							continue;

						Console.WriteLine("Found " + ancestor.GetPath());

						if (!chunker.MoveNext() && ancestors.Length == i + 1)
							yield return new NetQuery(walker.CurrentElement);
					}
				} while (walker.MoveNext());
			}
		}

		public IEnumerable<NetQuery> Parents()
		{
			return _elements[0].AncestorsAndSelf().Select(el => new NetQuery(el));
		}

		public string Text()
		{
			var els = _elements.Select(el => el.Value).ToArray();

			return String.Join(",", els);
		}

		public string Attr(string attributeName)
		{
			var els = _elements.Select(el => (string) el.Attribute(attributeName)).ToArray();

			return String.Join(",", els);
		}

		public string Html()
		{
			var els = _elements.Select(el => el.ToString()).ToArray();

			return String.Join("\n", els);
		}
	}	
}